#!/usr/bin/python

import asyncore
import collections
import fcntl
import json
import logging
import os
import socket
import string
import sys
import time
import types

"""
Class       : LoggerListener
Description : logger listern which inhreit asyncore Dispatcher
"""

class LoggerListener(asyncore.dispatcher):

    """
    unix_socket: unix socket for logger listener
    """
    __unix_socket      = None

    """
    unix_socket_path : unix socket file bind path
    """
    __unix_socket_path = ""

    """
    unix_listen_backlog : unix listener backlog
    """
    __unix_backlog     = 32

    """
    constructor: constructor method for logger listener
    """
    def __init__(self, unix_socket_path, unix_backlog = None):
        asyncore.dispatcher.__init__(self)
        """
        initialize unix socket path and unix backlog
        """
        self.__unix_socket_path = unix_socket_path
        if os.path.exists(self.__unix_socket_path):
            os.remove(self.__unix_socket_path)
        if unix_backlog is not None:
            os.__unix_backlog = unix_backlog

        """
        initialize parent class instance and some properties
        """
        self.__unix_socket = self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.bind(self.__unix_socket_path)
        self.listen(self.__unix_backlog)
        return

    """
    handle_accept: handler accept request for listern
    """
    def handle_accept(self):
        accepted_socket,address = self.accept()
        accepted_socket.settimeout(5)
        accepted_socket.setblocking(False)
        return LoggerRequestHandler(accepted_socket)


"""
Class       : LoggerRequestHandler:
Description : handler for logger request
"""

class LoggerRequestHandler(asyncore.dispatcher):

    """
    __write_end_signal: write end signal
    """
    __write_end_signal     = False

    """
    __write_logger_string: write logger string
    """
    __recv_logger_sequence = None

    """
    __recv_chunk_length: receive string chunk length
    """
    __recv_chunk_length    = 4096

    """
    constructor: constructor method for LggerRequestHandler
    """
    def __init__(self, sock, chunk_len = None):
        asyncore.dispatcher.__init__(self, sock)
        self.__write_end_signal     = False
        self.__recv_logger_sequence = collections.deque()
        if chunk_len is not None:
            self.__recv_chunk_len = chunk_len
        return


    """
    handle_read: read function for request handler
    """
    def handle_read(self):
        result = {"result":"OK","message":""}
        chunk_data = self.recv(self.__recv_chunk_length)
        if len(chunk_data) > 0:
            if chunk_data.find("\r\n\r\n"):
                self.__recv_logger_sequence.append(chunk_data.rstrip("\r\n\r\n"))
                self.__write_end_signal = True
                result = self.__handle_logger_request(self.__recv_logger_sequence)
                try:
                    self.send(json.dumps(result))
                except socket.error,e:
                    print ({"result":"ERROR","message":str(e)})
                finally:
                    self.close()
            else:
                self.__recv_logger_sequence.append(chunk_data)
        return


    """
    handle_request: handle request method
    """
    def __handle_logger_request(self, recv_deque):
        result = {"result":"OK","message":""}
        try:
            deque_len = len(recv_deque)
            if deque_len:
                i = 0
                request_message = ""
                for i in xrange(0, deque_len):
                    request_message += recv_deque[i]
                json_data = self.__translate_message_to_json(request_message)
                configuration = dict()
                configuration['logger']    = json_data['logger']
                configuration['file_name'] = json_data['file_name']
                LogWriter.log(configuration, json_data['message']) 
                del request_message
                del configuration
        except KeyError,e:
             print ("[ERROR][LoggerRequestHandle]" + str(e))
             result = {"result":"ERROR","message":str(e)}
        except ValueError,e:
             print ("[ERROR][LoggerREquestHandle]" + str(e))
             result = {"result":"ERROR","message":str(e)}
        except socket.error,e:
             print ("[ERROR][LoggerREquestHandle]" + str(e))
             result = {"result":"ERROR", "message":str(e)}
        except Exception,e:
             print ("[ERROR][LoggerREquestHandle]" + str(e))
             result = {"result":"ERROR","message":str(e)}
        return result


    """
    __translate_message_to_json: translate message to json structure
    """
    def __translate_message_to_json(self, message):
        return json.loads(message)


    """
    handle_close: handle while close socket
    """
    def handle_close(self):
        if self.__recv_logger_sequence is not None:
            self.__recv_logger_sequence.clear()
            self.__recv_logger_sequence = None
        return


"""
Request Logger Protocol:
{
    "logger"    : "LOGGER NAME"
    "file_name" : "LOGGER FILE NAME"
    "message"   : {
        "time"    : "LOG TIME"
        "level"   : "LOG LEVEL"
        "message" : "LOG REAL MESSAGE STRING"
    }
}
"""


"""
Class       : LoggerWriter
Description : logger protocol writer
"""
class LogWriter(object):

     """
     log: make up configuration and log message
     """
     @classmethod
     def log(cls, configuration, message):
         fileHandle = None
         log_list   = ""
         try:
             if message is None:
                 raise TypeError("parameter message can not be None type")
             if type(message) is not types.ListType:
                 raise TypeError("parameter message must be a string list")
             msg_len = len(message)
             if msg_len > 0 :
                 fileHandle = open(configuration['file_name'], "a")
                 fcntl.flock(fileHandle, fcntl.LOCK_EX)
                 loggerLevel  = logging.INFO
                 loggerLevels = [logging.INFO, logging.WARNING, logging.ERROR, logging.DEBUG, logging.CRITICAL, logging.NOTSET]
                 for i in xrange(0, msg_len):
                     if message[i]['level'] not in loggerLevels:
                         loggerLevel = logging.INFO
                     else:
                         loggerLevel = message[i]['level']
                     log_list += "[%s][%s][%s]:%s\r\n" % (message[i]['time'], configuration['logger'], cls._get_logging_level_name(loggerLevel),message[i]['message'])
                 fileHandle.write(log_list)
         except TypeError, e:
             print "[ERROR][LogWriter]" + str(e)
         except KeyError,e:
             print "[ERROR][LogWriter]" + str(e)
         except OSError,e:
             print "[ERROR][LogWriter]" + str(e)
         except Exception,e:
             print "[ERROR][LogWriter]" + str(e)
         finally:
             if fileHandle is not None:
                 fcntl.flock(fileHandle, fcntl.LOCK_UN)
                 fileHandle.close()
         return

     """
     _get_level_name: get level name from message level
     """
     @classmethod
     def _get_logging_level_name(cls, level):
         if level == logging.INFO:
             return "INFO"
         elif level == logging.WARNING:
             return "WARNING"
         elif level == logging.ERROR:
             return "ERROR"
         elif level == logging.DEBUG:
             return "DEBUG"
         elif level == logging.CRITICAL:
             return "CRITICAL"
         elif level == logging.NOTSET:
             return "INFO"
         else:
             return "INFO"


if __name__ == "__main__":

    pid = 0
    try:
        pid = os.fork()
        if pid > 0:
            print ("[PID %d]Parent Process Exit!......" % (os.getpid()))
            sys.exit(0)
    except OSError,e:
        print ("[PID %d]Fork child process failure, error message: %s" % (os.getpid(), str(e)))
        sys.exit(-1)

    os.umask(0)
    os.setuid(os.getuid())
    os.setsid()
    os.dup2(sys.stderr.fileno(), sys.stdin.fileno())
    os.close(sys.stdin.fileno())
    os.close(sys.stderr.fileno())

    try:
        pid = os.fork()
        if pid == 0:
            print ("[PID %d]Start LoggerListener......" % (os.getpid()))
            LoggerListener("/home/logger.sock")
            asyncore.loop(5, True)            
            sys.exit(0)
        elif pid > 0:
            while True:
                time.sleep(5)
                os.waitpid(pid, os.WNOHANG)
    except OSError,e:
        print ("[PID %d]Fork child process failure, error message: %s......" % (os.getpid(), str(e)))
        sys.exit(-1)
